package com.chris.getfile.comm;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;

/**
 * LocalDateTime 等 java8 时间类序列化和反序列化的支持
 * 主要针对 JacksonConfig
 * 2022-7-12 重新整理 苏斌
 */
//@Configuration
public class ObjectMapperUtil {


    /**
     * 重新创建 ObjectMapper 配置对象
     * 重要类， 全局注册一个 bean类 ，定义jackjson
     * 非常非常非常重要了类
     * @return
     */
    //@Bean(name = "app_objectMapper")
    //@Primary
    //@ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper objectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        //------------------------------------基础设置--------------------------------------
        // 指定要序列化的域，field,get和set,以及修饰符范围，ANY是都有包括private和public
         objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         //todo 会不会影响具体业务，有待观察确认
        //此处有坑和 JsonAutoDetect.Visibility.ANY《有冲突》spring-boot-starter-actuator <debug冲突>，会提示红字 ：：*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at s\src\java.instrument\share\native\libinstrument\JPLISAgent.c line: 876
        objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE);
        //此项目会生成 json class名称，禁用
        // om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        //设置当前时区
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        // 设置时区
        // objectMapper.setTimeZone(TimeZone.getDefault());
        //设置为中国上海时区
        //objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置格式化输出
        // objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        // 设置蛇形格式
        // objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        //单引号处理
        //.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);


        //----------------------反序列化---------------------------------------------
        //忽略未知的属性，导致所有的故障-->如果json中有新增的字段并且是--》被转化实体类类中不存在的，不报错
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        //例如json数据中多出字段，而对象中没有此字段。
        // 如果设置true，抛出异常，因为字段不对应；
        // false则忽略多出的字段，默认值为null，将其他字段反序列化成功(默认）
        objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,false);



        //null 不序列化
        //om.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);  //JsonSerialize.Inclusion.ALWAYS(默认）  JsonInclude.Include.NON_EMPTY （null 值不出现）
        //取消 对象为空报错--->考虑一下，还是取消，对象为null，应该由上层对象处理，不由om处理
        //objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false);



        //------------------------时间格式-----------------------------------
        // 解决jackson2无法反序列化LocalDateTime的问题
        // 只强制 LocalDateTime / LocalDate / LocalTime 序列化(下面配置指明）， 其他还是会自动转换为TIMESTAMPS
        //objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        //取消所有 TIMESTAMPS 转化
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //取消默认转换timestamps形式,false使用日期格式转换，true不使用日期转换，结果是时间的数值157113793535
        objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
        // Date 所有的日期格式统一样式： yyyy-MM-dd HH:mm:ss
        objectMapper.setDateFormat(new SimpleDateFormat(Constants.DEFAULT_DATE_TIME_FORMAT ));
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        //-----------------------正序列化-------------------------------------------
        //LocalDate + LocalTime
        //二选一 时间格式
        //(1)timestamp
        // javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer_fortimestamp());
        //(2)dateime
        javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_TIME_FORMAT)));

        //-----------------------反序列化-------------------------------------------
        //二选一 时间格式
        //(1)timestamp
        //javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer_fortimestamp());
        //(2)dateime
        javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(Constants.DEFAULT_TIME_FORMAT)));



        objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());





        return objectMapper;
    }

    public class Constants {

        /** 默认日期时间格式*/
        public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
        /** 默认日期格式*/
        public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
        /** 默认时间格式*/
        public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    }

    /**
     * 序列化
     */
    public class LocalDateTimeSerializer_fortimestamp extends JsonSerializer<LocalDateTime> {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            if (value != null) {
                long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                gen.writeNumber(timestamp);
            }
        }
    }

    /**
     * 反序列化
     */
    public class LocalDateTimeDeserializer_fortimestamp extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            long timestamp = p.getValueAsLong();
            if (timestamp > 0) {
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
            } else {
                return null;
            }
        }
    }
}
